In this tutorial, we will learn how to create a DTOs (Data Transfer Objects) class in the spring boot application and how to convert Entities to DTOs and vice versa using the ModelMapper library.
Learn more about the DTO pattern atUnderstanding Data Transfer Object Design Pattern
Data Transfer Object Design Pattern is a frequently used design pattern. It is basically used to pass data with multiple attributes in one shot from client to server, to avoid multiple calls to a remote server.
Another advantage of using DTOs on RESTful APIs written in Java (and on Spring Boot), is that they can help to hide implementation details of domain objects (JPA entities). Exposing entities through endpoints can become a security issue if we do not carefully handle what properties can be changed through what operations.
Let's start with introducing the ModelMapper Java library that we will use to convert Entity to DTO and vice versa.
ModelMapper aims to make object mapping easy by automatically determining how one object model maps to another, based on conventions, in the same way, that a human would - while providing a simple, refactoring-safe API for handling specific use cases.
Read more about the model mapper library at http://modelmapper.org/.
We will need this dependency in the pom.xml:
<dependency>
<groupId>org.modelmapper</groupId>
<artifactId>modelmapper</artifactId>
<version>2.3.5</version>
</dependency>
We will need this dependency in the pom.xml:
<dependency>
<groupId>org.modelmapper</groupId>
<artifactId>modelmapper</artifactId>
<version>2.3.5</version>
</dependency>
Let's create a Post entity class and add the following content to it:
package net.javaguides.springboot.model;
import java.util.HashSet;
import java.util.Set;
import jakarta.persistence.*;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@NoArgsConstructor
@AllArgsConstructor
@Entity
@Table(name = "posts", uniqueConstraints = {@UniqueConstraint(columnNames = {"title"})})
public class Post {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(name = "title")
private String title;
@Column(name = "description")
private String description;
@Column(name = "content")
private String content;
}
Let's create PostDto class and add the following content to it:
package net.javaguides.springboot.payload;
import java.util.HashSet;
import java.util.Set;
import lombok.Data;
@Data
public class PostDto {
private long id;
private String title;
private String description;
private String content;
}
Include only those details in the DTO class required for the client. The Entity and DTO fields look the same but make that you will add fields that are required to the client.
Let's create a PostRepository to talk with the database for the Post entity:
package com.springboot.blog.repository;
import com.springboot.blog.entity.Post;
import org.springframework.data.jpa.repository.JpaRepository;
public interface PostRepository extends JpaRepository {
}
In the service layer, we will only work with the Post entity, not PostDto class:
package net.javaguides.springboot.service;
import java.util.List;
import net.javaguides.springboot.model.Post;
public interface PostService {
List< Post> getAllPosts();
Post createPost(Post post);
Post updatePost(long id, Post post);
void deletePost(long id);
Post getPostById(long id);
}
package net.javaguides.springboot.service.impl;
import java.util.List;
import java.util.Optional;
import org.springframework.stereotype.Service;
import net.javaguides.springboot.exception.ResourceNotFoundException;
import net.javaguides.springboot.model.Post;
import net.javaguides.springboot.repository.PostResository;
import net.javaguides.springboot.service.PostService;
@Service
public class PostServiceImpl implements PostService{
private final PostResository postRepository;
public PostServiceImpl(PostResository postRepository) {
super();
this.postRepository = postRepository;
}
@Override
public List getAllPosts() {
return postRepository.findAll();
}
@Override
public Post createPost(Post post) {
return postRepository.save(post);
}
@Override
public Post updatePost(long id, Post postRequest) {
Post post = postRepository.findById(id)
.orElseThrow(() -> new ResourceNotFoundException("Post", "id", id));
post.setTitle(postRequest.getTitle());
post.setDescription(postRequest.getDescription());
post.setContent(postRequest.getContent());
return postRepository.save(post);
}
@Override
public void deletePost(long id) {
Post post = postRepository.findById(id)
.orElseThrow(() -> new ResourceNotFoundException("Post", "id", id));
postRepository.delete(post);
}
@Override
public Post getPostById(long id) {
Optional result = postRepository.findById(id);
if(result.isPresent()) {
return result.get();
}else {
throw new ResourceNotFoundException("Post", "id", id);
}
// Post post = postRepository.findById(id)
// .orElseThrow(() -> new ResourceNotFoundException("Post", "id", id));
//return post;
}
}
Note that we are not using an entity to DTO and vice versa logic in the service layer.
Let's configure ModelMapper class as Spring bean so that we can inject it into the controller class:
package net.javaguides.springboot;
import org.modelmapper.ModelMapper;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
@SpringBootApplication
public class SpringbootBlogApiApplication {
@Bean
public ModelMapper modelMapper() {
return new ModelMapper();
}
public static void main(String[] args) {
SpringApplication.run(SpringbootBlogApiApplication.class, args);
}
}
In the below PostController class, we have injected ModelMapper class and used it in different REST APIs for an entity to DTO, vice versa conversion:
package net.javaguides.springboot.contoller;
import java.util.List;
import java.util.stream.Collectors;
import org.modelmapper.ModelMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import net.javaguides.springboot.model.Post;
import net.javaguides.springboot.payload.ApiResponse;
import net.javaguides.springboot.payload.PostDto;
import net.javaguides.springboot.service.PostService;
@RestController
@RequestMapping("/api/posts")
public class PostController {
@Autowired
private ModelMapper modelMapper;
private PostService postService;
public PostController(PostService postService) {
super();
this.postService = postService;
}
@GetMapping
public List< PostDto> getAllPosts() {
return postService.getAllPosts().stream().map(post -> modelMapper.map(post, PostDto.class))
.collect(Collectors.toList());
}
@GetMapping("/{id}")
public ResponseEntity< PostDto> getPostById(@PathVariable(name = "id") Long id) {
Post post = postService.getPostById(id);
// convert entity to DTO
PostDto postResponse = modelMapper.map(post, PostDto.class);
return ResponseEntity.ok().body(postResponse);
}
@PostMapping
public ResponseEntity< PostDto> createPost(@RequestBody PostDto postDto) {
// convert DTO to entity
Post postRequest = modelMapper.map(postDto, Post.class);
Post post = postService.createPost(postRequest);
// convert entity to DTO
PostDto postResponse = modelMapper.map(post, PostDto.class);
return new ResponseEntity< PostDto>(postResponse, HttpStatus.CREATED);
}
// change the request for DTO
// change the response for DTO
@PutMapping("/{id}")
public ResponseEntity< PostDto> updatePost(@PathVariable long id, @RequestBody PostDto postDto) {
// convert DTO to Entity
Post postRequest = modelMapper.map(postDto, Post.class);
Post post = postService.updatePost(id, postRequest);
// entity to DTO
PostDto postResponse = modelMapper.map(post, PostDto.class);
return ResponseEntity.ok().body(postResponse);
}
@DeleteMapping("/{id}")
public ResponseEntity< ApiResponse> deletePost(@PathVariable(name = "id") Long id) {
postService.deletePost(id);
ApiResponse apiResponse = new ApiResponse(Boolean.TRUE, "Post deleted successfully", HttpStatus.OK);
return new ResponseEntity< ApiResponse>(apiResponse, HttpStatus.OK);
}
}
We have used ModelMapper in createPost() method to convert Entity to DTO and vice versa:
@PostMapping
public ResponseEntity< PostDto> createPost(@RequestBody PostDto postDto) {
// convert DTO to entity
Post postRequest = modelMapper.map(postDto, Post.class);
Post post = postService.createPost(postRequest);
// convert entity to DTO
PostDto postResponse = modelMapper.map(post, PostDto.class);
return new ResponseEntity< PostDto>(postResponse, HttpStatus.CREATED);
}
We have used ModelMapper in updatePost() method to convert Entity to DTO and vice versa:
// change the request for DTO
// change the response for DTO
@PutMapping("/{id}")
public ResponseEntity updatePost(@PathVariable long id, @RequestBody PostDto postDto) {
// convert DTO to Entity
Post postRequest = modelMapper.map(postDto, Post.class);
Post post = postService.updatePost(id, postRequest);
// entity to DTO
PostDto postResponse = modelMapper.map(post, PostDto.class);
return ResponseEntity.ok().body(postResponse);
}
We have used ModelMapper in getPostById() method to convert Entity to DTO and vice versa:
@GetMapping("/{id}")
public ResponseEntity getPostById(@PathVariable(name = "id") Long id) {
Post post = postService.getPostById(id);
// convert entity to DTO
PostDto postResponse = modelMapper.map(post, PostDto.class);
return ResponseEntity.ok().body(postResponse);
}
We have used ModelMapper in getAllPosts() method to convert Entity to DTO and vice versa:
@GetMapping
public List getAllPosts() {
return postService.getAllPosts().stream().map(post -> modelMapper.map(post, PostDto.class))
.collect(Collectors.toList());
}
This tutorial demonstrated how to do a conversion from Entity to DTO and from DTO to Entity in a Spring boot REST API project. We have used the model mapper library instead of writing these conversions by hand.